import {useEffect, useMemo, useRef, useState} from 'react';
import styled from '@emotion/styled';
import {AnimatePresence, motion} from 'framer-motion';

import {Button} from 'sentry/components/core/button';
import {ButtonBar} from 'sentry/components/core/button/buttonBar';
import {Stack} from 'sentry/components/core/layout';
import {Text} from 'sentry/components/core/text';
import {IconChevron, IconLink} from 'sentry/icons';
import {space} from 'sentry/styles/space';
import {MarkedText} from 'sentry/utils/marked/markedText';
import {useNavigate} from 'sentry/utils/useNavigate';
import useOrganization from 'sentry/utils/useOrganization';
import useProjects from 'sentry/utils/useProjects';

import type {Block, TodoItem} from './types';
import {buildToolLinkUrl, getToolsStringFromBlock, postProcessLLMMarkdown} from './utils';

interface BlockProps {
  block: Block;
  blockIndex: number;
  isAwaitingFileApproval?: boolean;
  isAwaitingQuestion?: boolean;
  isFocused?: boolean;
  isLast?: boolean;
  isLatestTodoBlock?: boolean;
  isPolling?: boolean;
  onClick?: () => void;
  onDelete?: () => void;
  onMouseEnter?: () => void;
  onMouseLeave?: () => void;
  onNavigate?: () => void;
  onRegisterEnterHandler?: (
    handler: (key: 'Enter' | 'ArrowUp' | 'ArrowDown') => boolean
  ) => void;
  ref?: React.Ref<HTMLDivElement>;
}

function hasValidContent(content: string): boolean {
  if (!content) {
    return false;
  }
  const trimmed = content.trim();
  return trimmed.length > 0 && trimmed !== '.'; // sometimes the LLM just says '.' when calling a tool
}

/**
 * Convert todos to markdown format
 */
function todosToMarkdown(todos: TodoItem[]): string {
  return todos
    .map(todo => {
      const checkbox = todo.status === 'completed' ? '[x]' : '[ ]';
      const content =
        todo.status === 'completed'
          ? `~~${todo.content}~~`
          : todo.status === 'in_progress'
            ? `_${todo.content}_`
            : todo.content;
      return `${checkbox} ${content}`;
    })
    .join('  \n');
}

/**
 * Determine the dot color based on tool execution status
 */
function getToolStatus(
  block: Block
): 'loading' | 'content' | 'success' | 'failure' | 'mixed' | 'pending' {
  if (block.loading) {
    return 'loading';
  }

  // Check tool_links for empty_results metadata
  const toolLinks = block.tool_links || [];
  const toolCalls = block.message.tool_calls || [];
  const hasTools = toolCalls.length > 0;

  if (hasTools) {
    // Check if any tool has pending approval or pending question
    const hasPending = toolLinks.some(
      link => link?.params?.pending_approval || link?.params?.pending_question
    );
    if (hasPending) {
      return 'pending';
    }

    if (toolLinks.length === 0) {
      // No metadata available, assume success
      return 'success';
    }

    let hasSuccess = false;
    let hasFailure = false;

    toolLinks.forEach(link => {
      if (link?.params?.empty_results === true) {
        hasFailure = true;
      } else if (link !== null) {
        hasSuccess = true;
      }
    });

    if (hasFailure && hasSuccess) {
      return 'mixed';
    }
    if (hasFailure) {
      return 'failure';
    }
    return 'success';
  }

  // No tools, check if there's content
  const hasContent = hasValidContent(block.message.content);
  if (hasContent) {
    return 'content';
  }

  return 'success';
}

function BlockComponent({
  block,
  blockIndex: _blockIndex,
  isAwaitingFileApproval,
  isAwaitingQuestion,
  isLast,
  isLatestTodoBlock,
  isFocused,
  isPolling,
  onClick,
  onDelete,
  onMouseEnter,
  onMouseLeave,
  onNavigate,
  onRegisterEnterHandler,
  ref,
}: BlockProps) {
  const organization = useOrganization();
  const navigate = useNavigate();
  const {projects} = useProjects();
  const toolsUsed = getToolsStringFromBlock(block);
  const hasTools = toolsUsed.length > 0;
  const hasContent = hasValidContent(block.message.content);

  const processedContent = useMemo(
    () => postProcessLLMMarkdown(block.message.content),
    [block.message.content]
  );

  // State to track selected tool link (for navigation)
  const [selectedLinkIndex, setSelectedLinkIndex] = useState(0);
  const selectedLinkIndexRef = useRef(selectedLinkIndex);

  // Keep ref in sync with state
  useEffect(() => {
    selectedLinkIndexRef.current = selectedLinkIndex;
  }, [selectedLinkIndex]);

  // Get valid tool links sorted by their corresponding tool call indices
  // Also create a mapping from tool call index to sorted link index
  const {sortedToolLinks, toolCallToLinkIndexMap} = useMemo(() => {
    const mappedLinks = (block.tool_links || [])
      .map((link, idx) => {
        if (!link) {
          return null;
        }

        // get tool_call_id from tool_results, which we expect to be aligned with tool_links.
        const toolCallId = block.tool_results?.[idx]?.tool_call_id;
        const toolCallIndex = block.message.tool_calls?.findIndex(
          call => call.id === toolCallId
        );
        const canBuildUrl = buildToolLinkUrl(link, organization.slug, projects) !== null;

        if (toolCallIndex !== undefined && toolCallIndex >= 0 && canBuildUrl) {
          return {link, toolCallIndex};
        }
        return null;
      })
      .filter(
        (
          item
        ): item is {
          link: {kind: string; params: Record<string, any>};
          toolCallIndex: number;
        } => item !== null
      )
      .sort((a, b) => a.toolCallIndex - b.toolCallIndex);

    // Create mapping from tool call index to sorted link index
    const toolCallToLinkMap = new Map<number, number>();
    mappedLinks.forEach((item, sortedIndex) => {
      toolCallToLinkMap.set(item.toolCallIndex, sortedIndex);
    });

    return {
      sortedToolLinks: mappedLinks.map(item => item.link),
      toolCallToLinkIndexMap: toolCallToLinkMap,
    };
  }, [
    block.tool_links,
    block.tool_results,
    block.message.tool_calls,
    organization.slug,
    projects,
  ]);

  const hasValidLinks = sortedToolLinks.length > 0;

  // Reset selected index when block changes or when there are no valid links
  useEffect(() => {
    if (!hasValidLinks) {
      setSelectedLinkIndex(0);
    } else if (selectedLinkIndex >= sortedToolLinks.length) {
      setSelectedLinkIndex(0);
    }
  }, [hasValidLinks, selectedLinkIndex, sortedToolLinks.length]);

  // Register the key handler with the parent
  useEffect(() => {
    const handler = (key: 'Enter' | 'ArrowUp' | 'ArrowDown') => {
      if (!hasValidLinks) {
        return false;
      }

      if (key === 'ArrowUp') {
        // Move to previous link
        const currentIndex = selectedLinkIndexRef.current;
        if (currentIndex > 0) {
          // Can move up within this block's links
          setSelectedLinkIndex(prev => prev - 1);
          return true;
        }
        // At the first link, let navigation move to previous block
        return false;
      }

      if (key === 'ArrowDown') {
        // Move to next link
        const currentIndex = selectedLinkIndexRef.current;
        if (currentIndex < sortedToolLinks.length - 1) {
          // Can move down within this block's links
          setSelectedLinkIndex(prev => prev + 1);
          return true;
        }
        // At the last link, let navigation move to next block
        return false;
      }

      if (key === 'Enter') {
        // Navigate to selected link using ref to get current value
        const currentIndex = selectedLinkIndexRef.current;
        const selectedLink = sortedToolLinks[currentIndex];
        if (selectedLink) {
          const url = buildToolLinkUrl(selectedLink, organization.slug, projects);
          if (url) {
            navigate(url);
          }
        }
        return true;
      }
      return false;
    };

    onRegisterEnterHandler?.(handler);
  }, [
    hasValidLinks,
    sortedToolLinks,
    organization.slug,
    projects,
    navigate,
    onRegisterEnterHandler,
  ]);

  const handleDeleteClick = (e: React.MouseEvent) => {
    e.stopPropagation();
    onDelete?.();
  };

  const handleNavigateClick = (e: React.MouseEvent, linkIndex: number) => {
    e.stopPropagation();
    if (sortedToolLinks.length === 0) {
      return;
    }

    // Navigate to the clicked link
    const selectedLink = sortedToolLinks[linkIndex];
    if (selectedLink) {
      const url = buildToolLinkUrl(selectedLink, organization.slug, projects);
      if (url) {
        navigate(url);
        onNavigate?.();
      }
    }
  };

  const showActions =
    isFocused && !block.loading && !isAwaitingFileApproval && !isAwaitingQuestion;

  return (
    <Block
      ref={ref}
      isFocused={isFocused}
      isLast={isLast}
      onClick={onClick}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
    >
      <AnimatePresence>
        <motion.div
          initial={{opacity: 0, y: 10}}
          animate={{opacity: 1, y: 0}}
          exit={{opacity: 0, y: 10}}
        >
          {block.message.role === 'user' ? (
            <BlockRow>
              <BlockChevronIcon direction="right" size="sm" />
              <UserBlockContent>{block.message.content ?? ''}</UserBlockContent>
            </BlockRow>
          ) : (
            <BlockRow>
              <ResponseDot
                status={getToolStatus(block)}
                hasOnlyTools={!hasContent && hasTools}
              />
              <BlockContentWrapper hasOnlyTools={!hasContent && hasTools}>
                {hasContent && (
                  <BlockContent
                    text={processedContent}
                    onClick={(e: React.MouseEvent<HTMLDivElement>) => {
                      // Intercept clicks on links to use client-side navigation
                      const anchor = (e.target as HTMLElement).closest('a');
                      if (anchor) {
                        e.preventDefault();
                        e.stopPropagation();
                        const href = anchor.getAttribute('href');
                        if (href?.startsWith('/')) {
                          navigate(href);
                          onNavigate?.();
                        }
                      }
                    }}
                  />
                )}
                {hasTools && (
                  <ToolCallStack gap="md">
                    {block.message.tool_calls?.map((toolCall, idx) => {
                      const toolString = toolsUsed[idx];
                      const hasLink = toolCallToLinkIndexMap.has(idx);
                      // Check if this tool call corresponds to the selected link
                      const correspondingLinkIndex = toolCallToLinkIndexMap.get(idx);
                      const isHighlighted =
                        isFocused &&
                        hasValidLinks &&
                        correspondingLinkIndex !== undefined &&
                        correspondingLinkIndex === selectedLinkIndex;
                      const isTodoWriteCall = toolCall.function === 'todo_write';
                      const showTodoList =
                        isTodoWriteCall &&
                        isLatestTodoBlock &&
                        block.todos &&
                        block.todos.length > 0;

                      return (
                        <ToolCallWithTodos key={`${toolCall.function}-${idx}`}>
                          <ToolCallTextContainer>
                            <ToolCallText
                              size="xs"
                              variant="muted"
                              monospace
                              isHighlighted={isHighlighted}
                            >
                              {toolString}
                            </ToolCallText>
                            {hasLink && (
                              <ToolCallLinkIcon size="xs" isHighlighted={isHighlighted} />
                            )}
                          </ToolCallTextContainer>
                          {showTodoList && (
                            <TodoListContent text={todosToMarkdown(block.todos!)} />
                          )}
                        </ToolCallWithTodos>
                      );
                    })}
                  </ToolCallStack>
                )}
              </BlockContentWrapper>
            </BlockRow>
          )}
          <AnimatePresence>
            {showActions && (
              <motion.div
                initial={{opacity: 0, y: 5}}
                animate={{opacity: 1, y: 0}}
                exit={{opacity: 0, y: 5}}
                transition={{duration: 0.1}}
              >
                <ActionButtonBar gap="sm">
                  {!isPolling && (
                    <Button size="xs" priority="default" onClick={handleDeleteClick}>
                      Rethink from here ⌫
                    </Button>
                  )}
                  {hasValidLinks && (
                    <ButtonBar merged gap="0">
                      {sortedToolLinks.map((_, idx) => (
                        <Button
                          key={idx}
                          size="xs"
                          priority={idx === selectedLinkIndex ? 'primary' : 'default'}
                          onClick={e => handleNavigateClick(e, idx)}
                          onMouseEnter={() => setSelectedLinkIndex(idx)}
                        >
                          {idx === 0
                            ? sortedToolLinks.length === 1
                              ? 'Navigate'
                              : 'Navigate #1'
                            : `#${idx + 1}`}
                          {idx === selectedLinkIndex && ' ⏎'}
                        </Button>
                      ))}
                    </ButtonBar>
                  )}
                </ActionButtonBar>
              </motion.div>
            )}
          </AnimatePresence>
        </motion.div>
      </AnimatePresence>
    </Block>
  );
}

BlockComponent.displayName = 'BlockComponent';

export default BlockComponent;

const Block = styled('div')<{isFocused?: boolean; isLast?: boolean}>`
  width: 100%;
  border-top: 1px solid transparent;
  border-bottom: ${p =>
    p.isLast ? '1px solid transparent' : `1px solid ${p.theme.border}`};
  position: relative;
  flex-shrink: 0; /* Prevent blocks from shrinking */
  cursor: pointer;
  background: ${p => (p.isFocused ? p.theme.hover : 'transparent')};
`;

const BlockRow = styled('div')`
  display: flex;
  align-items: flex-start;
  width: 100%;
`;

const BlockChevronIcon = styled(IconChevron)`
  color: ${p => p.theme.subText};
  margin-top: 18px;
  margin-left: ${space(2)};
  margin-right: ${space(1)};
  flex-shrink: 0;
`;

const ResponseDot = styled('div')<{
  status: 'loading' | 'content' | 'success' | 'failure' | 'mixed' | 'pending';
  hasOnlyTools?: boolean;
}>`
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-top: ${p => (p.hasOnlyTools ? '12px' : '22px')};
  margin-left: ${space(2)};
  flex-shrink: 0;
  background: ${p => {
    switch (p.status) {
      case 'loading':
        return p.theme.pink400;
      case 'pending':
        return p.theme.pink400;
      case 'content':
        return p.theme.purple400;
      case 'success':
        return p.theme.green400;
      case 'failure':
        return p.theme.red400;
      case 'mixed':
        return p.theme.yellow400;
      default:
        return p.theme.purple400;
    }
  }};

  ${p =>
    p.status === 'loading' &&
    `
    animation: blink 1s infinite;

    @keyframes blink {
      0%, 50% { opacity: 1; }
      51%, 100% { opacity: 0.3; }
    }
  `}
`;

const BlockContentWrapper = styled('div')<{hasOnlyTools?: boolean}>`
  padding: ${p =>
    p.hasOnlyTools ? `${p.theme.space.md} ${p.theme.space.xl}` : p.theme.space.xl};
  flex: 1;
  min-width: 0;
  overflow: hidden;
`;

const BlockContent = styled(MarkedText)`
  width: 100%;
  color: ${p => p.theme.textColor};
  white-space: pre-wrap;
  word-wrap: break-word;
  padding-bottom: 0;
  margin-bottom: -${space(1)};

  p,
  li,
  ul {
    margin: -${space(1)} 0;
  }

  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    margin: 0;
    font-size: ${p => p.theme.fontSize.lg};
  }

  p:first-child,
  li:first-child,
  ul:first-child,
  h1:first-child,
  h2:first-child,
  h3:first-child,
  h4:first-child,
  h5:first-child,
  h6:first-child {
    margin-top: 0;
  }
`;

const UserBlockContent = styled('div')`
  width: 100%;
  padding: ${space(2)} ${space(2)} ${space(2)} 0;
  white-space: pre-wrap;
  word-wrap: break-word;
  color: ${p => p.theme.subText};
`;

const ToolCallStack = styled(Stack)`
  width: 100%;
  min-width: 0;
  padding-right: ${p => p.theme.space.lg};
`;

const ToolCallWithTodos = styled('div')`
  display: flex;
  flex-direction: column;
  gap: ${p => p.theme.space.xs};
`;

const ToolCallTextContainer = styled('div')`
  display: inline-flex;
  align-items: center;
  gap: ${p => p.theme.space.xs};
  max-width: 100%;
`;

const ToolCallText = styled(Text)<{isHighlighted?: boolean}>`
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  ${p =>
    p.isHighlighted &&
    `
    color: ${p.theme.linkHoverColor};
    font-weight: ${p.theme.fontWeight.bold};
  `}
`;

const ToolCallLinkIcon = styled(IconLink)<{isHighlighted?: boolean}>`
  color: ${p => (p.isHighlighted ? p.theme.linkHoverColor : p.theme.subText)};
`;

const ActionButtonBar = styled(ButtonBar)`
  position: absolute;
  bottom: ${p => p.theme.space['2xs']};
  right: ${p => p.theme.space.md};
  white-space: nowrap;
  font-size: ${p => p.theme.fontSize.sm};
  background: ${p => p.theme.background};
`;

const TodoListContent = styled(MarkedText)`
  margin-top: ${p => p.theme.space.xs};
  margin-bottom: -${p => p.theme.space.xl};
  font-size: ${p => p.theme.fontSize.xs};
  font-family: ${p => p.theme.text.familyMono};
  color: ${p => p.theme.subText};
`;
